<?php
/**
 * PureMVC Port to PHP originally translated by Asbjørn Sloth Tønnesen
 *
 * @author Omar Gonzalez :: omar@almerblank.com
 * @author Hasan Otuome :: hasan@almerblank.com
 *
 * Created on Sep 24, 2008
 * PureMVC - Copyright(c) 2006-2008 Futurescale, Inc., Some rights reserved.
 * Your reuse is governed by the Creative Commons Attribution 3.0 Unported License
 */

/**
 * A base Singleton <code>IFacade</code> implementation.
 *
 * <P>
 * In PureMVC, the <code>Facade</code> class assumes these
 * responsibilities:
 * <UL>
 * <LI>Initializing the <code>Model</code>, <code>View</code>
 * and <code>Controller</code> Singletons.</LI>
 * <LI>Providing all the methods defined by the <code>IModel,
 * PureMVC_View_Interface, & IController</code> interfaces.</LI>
 * <LI>Providing the ability to override the specific <code>Model</code>,
 * <code>View</code> and <code>Controller</code> Singletons created.</LI>
 * <LI>Providing a single point of contact to the application for
 * registering <code>Commands</code> and notifying <code>Observers</code></LI>
 * </UL>
 *
 * @see org.puremvc.php.core.Model Model
 * @see org.puremvc.php.core.View View
 * @see org.puremvc.php.core.Controller Controller
 * @see org.puremvc.php.patterns.observer.Notification Notification
 * @see org.puremvc.php.patterns.mediator.Mediator Mediator
 * @see org.puremvc.php.patterns.proxy.Proxy Proxy
 * @see org.puremvc.php.patterns.command.SimpleCommand SimpleCommand
 * @see org.puremvc.php.patterns.command.MacroCommand MacroCommand
 */
class PureMVC_Facade implements PureMVC_Facade_Interface
{
	// Private references to Model, View and Controller
	protected $_controller;
	protected $_model;
	protected $_view;

	// The Singleton Facade instance.
	protected static $_instance;
	/**
	 * Constructor.
	 *
	 * <P>
	 * This <code>IFacade</code> implementation is a Singleton,
	 * so you should not call the constructor
	 * directly, but instead call the static Singleton
	 * Factory method <code>Facade.getInstance()</code>
	 *
	 * @throws Error Error if Singleton instance has already been constructed
	 *
	 */
	protected function __construct()
	{
		$this->_initializeFacade();
	}

	/**
	 * Initialize the Singleton <code>Facade</code> instance.
	 *
	 * <P>
	 * Called automatically by the constructor. Override in your
	 * subclass to do any subclass specific initializations. Be
	 * sure to call <code>super.initializeFacade()</code>, though.</P>
	 */
	protected function _initializeFacade()
	{
		$this->_initializeModel();
		$this->_initializeController();
		$this->_initializeView();
	}

	/**
	 * Facade Singleton Factory method
	 *
	 * @return the Singleton instance of the Facade
	 */
	static public function getInstance()
	{
		if ( self::$_instance == null )
			self::$_instance = new self();
		return self::$_instance;
	}

	/**
	 * Initialize the <code>Controller</code>.
	 *
	 * <P>
	 * Called by the <code>initializeFacade</code> method.
	 * Override this method in your subclass of <code>Facade</code>
	 * if one or both of the following are true:
	 * <UL>
	 * <LI> You wish to initialize a different <code>IController</code>.</LI>
	 * <LI> You have <code>Commands</code> to register with the <code>Controller</code> at startup.</code>. </LI>
	 * </UL>
	 * If you don't want to initialize a different <code>IController</code>,
	 * call <code>super.initializeController()</code> at the beginning of your
	 * method, then register <code>Command</code>s.
	 * </P>
	 */
	protected function _initializeController()
	{
		if ( $this->_controller != null )
			return;
		$this->_controller = PureMVC_Controller::getInstance();
	}

	/**
	 * Initialize the <code>Model</code>.
	 *
	 * <P>
	 * Called by the <code>initializeFacade</code> method.
	 * Override this method in your subclass of <code>Facade</code>
	 * if one or both of the following are true:
	 * <UL>
	 * <LI> You wish to initialize a different <code>IModel</code>.</LI>
	 * <LI> You have <code>Proxy</code>s to register with the Model that do not
	 * retrieve a reference to the Facade at construction time.</code></LI>
	 * </UL>
	 * If you don't want to initialize a different <code>IModel</code>,
	 * call <code>super.initializeModel()</code> at the beginning of your
	 * method, then register <code>Proxy</code>s.
	 * <P>
	 * Note: This method is <i>rarely</i> overridden; in practice you are more
	 * likely to use a <code>Command</code> to create and register <code>Proxy</code>s
	 * with the <code>Model</code>, since <code>Proxy</code>s with mutable data will likely
	 * need to send <code>PureMVC_Observer_Notification_Interface</code>s and thus will likely want to fetch a reference to
	 * the <code>Facade</code> during their construction.
	 * </P>
	 */
	protected function _initializeModel()
	{
		if ( $this->_model != null )
			return;
		$this->_model = PureMVC_Model::getInstance();
	}

	/**
	 * Initialize the <code>View</code>.
	 *
	 * <P>
	 * Called by the <code>initializeFacade</code> method.
	 * Override this method in your subclass of <code>Facade</code>
	 * if one or both of the following are true:
	 * <UL>
	 * <LI> You wish to initialize a different <code>PureMVC_View_Interface</code>.</LI>
	 * <LI> You have <code>Observers</code> to register with the <code>View</code></LI>
	 * </UL>
	 * If you don't want to initialize a different <code>PureMVC_View_Interface</code>,
	 * call <code>super.initializeView()</code> at the beginning of your
	 * method, then register <code>IMediator</code> instances.
	 * <P>
	 * Note: This method is <i>rarely</i> overridden; in practice you are more
	 * likely to use a <code>Command</code> to create and register <code>Mediator</code>s
	 * with the <code>View</code>, since <code>IMediator</code> instances will need to send
	 * <code>PureMVC_Observer_Notification_Interface</code>s and thus will likely want to fetch a reference
	 * to the <code>Facade</code> during their construction.
	 * </P>
	 */
	protected function _initializeView()
	{
		if ( $this->_view != null )
			return;
		$this->_view = PureMVC_View::getInstance();
	}

	/**
	 * Notify <code>Observer</code>s.
	 *
	 * @param notification the <code>PureMVC_Observer_Notification_Interface</code> to have the <code>View</code> notify <code>Observers</code> of.
	 */
	public function notifyObservers( PureMVC_Observer_Notification_Interface $notification )
	{
		if ( $this->_view != null )
			$this->_view->notifyObservers( $notification );
	}

	/**
	 * Register an <code>PureMVC_Command_Interface</code> with the <code>Controller</code> by Notification name.
	 *
	 * @param notificationName the name of the <code>PureMVC_Observer_Notification_Interface</code> to associate the <code>PureMVC_Command_Interface</code> with
	 * @param commandClassRef a reference to the Class of the <code>PureMVC_Command_Interface</code>
	 */
	public function registerCommand( $notificationName, $commandClassRef )
	{
		$this->_controller->registerCommand( $notificationName, $commandClassRef );
	}

	/**
	 * Remove a previously registered <code>PureMVC_Command_Interface</code> to <code>PureMVC_Observer_Notification_Interface</code> mapping from the Controller.
	 *
	 * @param notificationName the name of the <code>PureMVC_Observer_Notification_Interface</code> to remove the <code>PureMVC_Command_Interface</code> mapping for
	 */
	public function removeCommand( $notificationName )
	{
		$this->_controller->removeCommand( $notificationName );
	}

	/**
	 * Check if a Command is registered for a given Notification
	 *
	 * @param notificationName
	 * @return whether a Command is currently registered for the given <code>notificationName</code>.
	 */
	public function hasCommand( $notificationName )
	{
		return $this->_controller->hasCommand( $notificationName );
	}

	/**
	 * Register an <code>PureMVC_Proxy_Interface</code> with the <code>Model</code> by name.
	 *
	 * @param proxyName the name of the <code>PureMVC_Proxy_Interface</code>.
	 * @param proxy the <code>PureMVC_Proxy_Interface</code> instance to be registered with the <code>Model</code>.
	 */
	public function registerProxy( PureMVC_Proxy_Interface $proxy )
	{
		$this->_model->registerProxy( $proxy );
	}

	/**
	 * Retrieve an <code>PureMVC_Proxy_Interface</code> from the <code>Model</code> by name.
	 *
	 * @param proxyName the name of the proxy to be retrieved.
	 * @return the <code>PureMVC_Proxy_Interface</code> instance previously registered with the given <code>proxyName</code>.
	 */
	public function retrieveProxy( $proxyName )
	{
		return $this->_model->retrieveProxy( $proxyName );
	}

	/**
	 * Check to see if a Proxy is registered with the Model.
	 *
	 * @param proxyName name of the <code>PureMVC_Proxy_Interface</code> instance to check for.
	 */
	public function hasProxy( $proxyName )
	{
		return $this->_model->hasProxy( $proxyName );
	}

	/**
	 * Remove an <code>PureMVC_Proxy_Interface</code> from the <code>Model</code> by name.
	 *
	 * @param proxyName the <code>PureMVC_Proxy_Interface</code> to remove from the <code>Model</code>.
	 */
	public function removeProxy( $proxyName )
	{
		if ( $this->_model != null )
			$proxy = $this->_model->removeProxy( $proxyName );
		return $proxy;
	}

	/**
	 * Register a <code>IMediator</code> with the <code>View</code>.
	 *
	 * @param mediatorName the name to associate with this <code>IMediator</code>
	 * @param mediator a reference to the <code>IMediator</code>
	 */
	public function registerMediator( PureMVC_Mediator_Interface $mediator )
	{
		if ( $this->_view != null )
			$this->_view->registerMediator( $mediator );
	}

	/**
	 * Retrieve an <code>IMediator</code> from the <code>View</code>.
	 *
	 * @param mediatorName
	 * @return the <code>IMediator</code> previously registered with the given <code>mediatorName</code>.
	 */
	public function retrieveMediator( $mediatorName )
	{
		return $this->_view->retrieveMediator( $mediatorName );
	}

	/**
	 * Check to see if a Mediator is registered with the View.
	 *
	 * @param mediatorName name of the <code>IMediator</code> instance to check for.
	 */
	public function hasMediator( $mediatorName )
	{
		return $this->_view->hasMediator( $mediatorName );
	}

	/**
	 * Remove an <code>IMediator</code> from the <code>View</code>.
	 *
	 * @param mediatorName name of the <code>IMediator</code> to be removed.
	 */
	public function removeMediator( $mediatorName )
	{
		if ( $this->_view != null )
			$mediator = $this->_view->removeMediator( $mediatorName );
		return $mediator;
	}

	/**
	 * Send an <code>PureMVC_Observer_Notification_Interface</code>.
	 *
	 * <P>
	 * Keeps us from having to construct new notification
	 * instances in our implementation code.
	 * @param notificationName the name of the notiification to send
	 * @param body the body of the notification (optional)
	 * @param type the type of the notification (optional)
	 */
	public function sendNotification( $notificationName, $body = null, $type = null )
	{
		$notification = new PureMVC_Observer_Notification( $notificationName, $body, $type );
		$this->notifyObservers( $notification );
	}
}
